package com.zhangsr.mongodb.client.driver;

import com.mongodb.*;
import com.zhangsr.mongodb.client.constants.MongoConstants;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Author: zhangsr
 */
public class MongoDBDriver {

    public static final Log logger = LogFactory.getLog(MongoConstants.COMMON_MONGO);

    private DB database;
    /**
     * MongoOptions对象
     */
    private MongoOptions mongoOptions = null;
    /**
     * Mongo对象
     */
    private Mongo mongo = null;
    /**
     * 配置文件名
     */
    private String configFile;
    /**
     * 配置内容
     */
    private Properties config;
    /**
     * 数据库地址
     */
    private String hostname = MongoConstants.DEFAULT_MONGO_DB_HOSTNAME;
    /**
     * 数据库端口
     */
    private String port = MongoConstants.DEFAULT_MONGO_DB_PORT;
    /**
     * Mongo的连接数
     */
    private String connectionsPerHost = MongoConstants.DEFAULT_CONNECTIONS_PER_HOST;
    /**
     * 连接池中最低连接数量
     */
    private String minPoolsSize = MongoConstants.DEFAULT_MIN_POOL_SIZE;
    /**
     * DB
     */
    private String user = null;
    private String password = null;
    private String dataBaseName = MongoConstants.DEFAULT_MONGO_DB_DATABASE_NAME;
    /**
     * Mongo的Thread堵塞数
     */
    private String threadsAllowedToBlockForConnectionMultiplier = MongoConstants.DEFAULT_THREADSALLOWEDTOBLOCK_FOR_CONNECTIONMULTIPLIER;
    private String maxWaitTime = MongoConstants.DEFAULT_MAX_WAIT_TIME;
    private String connectTimeout = MongoConstants.DEFAULT_CONNECT_TIMEOUT;
    private String socketTimeout = MongoConstants.DEFAULT_SOCKET_TIMEOUT;
    private String autoConnectRetry = MongoConstants.DEFAULT_AUTO_CONNECT_RETRY;

    private String safe = MongoConstants.DEFAULT_MONGO_DB_SAFE;
    private String slaveOk = MongoConstants.DEFAULT_MONGO_SLAVE_OK;

    /**
     * 初始化mongo
     */
    public void init() {
        if (config != null)
            initFields(config);
        initMongoOptions();
        initMongo();
        initDatabase();
    }

    /**
     * 关闭mongo
     */
    public void close() {
        mongo.close();
    }

    /**
     * 为MongoDBDriver初始化属性
     * 属性配置文件 mongo-config.properties
     *
     * @param config 初始化属性值
     */
    private void initFields(Properties config) {
        Class<?> c = MongoDBDriver.class;
        for (Field f : c.getDeclaredFields()) {
            String key = c.getName() + "." + f.getName();
            String value = config.getProperty(key);
            if (StringUtils.isNotBlank(value.trim())) {
                f.setAccessible(true);
                try {
                    f.set(this, value);
                } catch (Exception e) {
                    logger.error("初始化MongoDBDriver参数[" + f.getName() + "=" + value + "]失败", e);
                }
            }
        }
    }

    private void initMongoOptions() {
        try {
            MongoOptions options = new MongoOptions();
            options.connectionsPerHost = Integer.valueOf(this.connectionsPerHost);
            options.threadsAllowedToBlockForConnectionMultiplier = Integer.valueOf(this.threadsAllowedToBlockForConnectionMultiplier);
//            options.minPoolsSize = Integer.valueOf(this.minPoolsSize);
            options.maxWaitTime = Integer.valueOf(this.maxWaitTime);
            options.connectTimeout = Integer.valueOf(this.connectTimeout);
            options.socketTimeout = Integer.valueOf(this.socketTimeout);
            options.autoConnectRetry = Boolean.valueOf(this.autoConnectRetry);
            options.safe = Boolean.valueOf(this.safe);
            this.mongoOptions = options;
        } catch (Exception e) {
            logger.error("初始化initMongoOptions失败", e);
        }
    }

    /**
     * 初始化mongo,database和collection实例
     */
    private void initMongo() {
        try {
            if (!hostname.contains(",")) {
                ServerAddress address = new ServerAddress(hostname, Integer.valueOf(port));
                this.mongo = new Mongo(address, this.mongoOptions);
            } else {
                String[] hostNames = hostname.split(",");
                List<ServerAddress> addresses = new ArrayList<ServerAddress>();
                String[] ports = port.split(",");
                if (ports.length != hostNames.length) {
                    throw new RuntimeException("初始化initMongo失败.");
                } else {
                    int i = 0;
                    for (String host : hostNames) {
                        addresses.add(new ServerAddress(host, Integer.valueOf(ports[i++])));
                    }
                }
                this.mongo = new Mongo(addresses, this.mongoOptions);
                if (Boolean.valueOf(slaveOk))
                    mongo.setReadPreference(ReadPreference.secondaryPreferred());
            }
        } catch (Exception e) {
            logger.error("初始化initMongo失败", e);
        }
    }

    private void initDatabase() {
        try {
            database = mongo.getDB(dataBaseName);
            if (StringUtils.isNotBlank(this.user) && StringUtils.isNotBlank(this.password)) {
                database.authenticate(user, password.toCharArray());
            }
        } catch (Exception e) {
            logger.error("初始化initMongo失败", e);
        }
    }

    //------------------------ setter ----------------------

    public void setConfigFile(String configFile) {
        this.configFile = configFile;
        Properties config = new Properties();
        try {
            config.load(MongoDBDriver.class.getClassLoader().getResourceAsStream(this.configFile));
            this.config = config;
        } catch (IOException e) {
            logger.error("无法加载配置文件:" + configFile, e);
        }
    }

    public void setConfig(Properties config) {
        this.config = config;
    }

    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    public void setPort(String port) {
        this.port = port;
    }

    public void setConnectionsPerHost(String connectionsPerHost) {
        this.connectionsPerHost = connectionsPerHost;
    }

    public void setMinPoolsSize(String minPoolsSize) {
        this.minPoolsSize = minPoolsSize;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setDataBaseName(String dataBaseName) {
        this.dataBaseName = dataBaseName;
    }

    public void setThreadsAllowedToBlockForConnectionMultiplier(String threadsAllowedToBlockForConnectionMultiplier) {
        this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
    }

    public void setMaxWaitTime(String maxWaitTime) {
        this.maxWaitTime = maxWaitTime;
    }

    public void setConnectTimeout(String connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public void setSocketTimeout(String socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    public void setAutoConnectRetry(String autoConnectRetry) {
        this.autoConnectRetry = autoConnectRetry;
    }

    public void setSlaveOk(String slaveOk) {
        this.slaveOk = slaveOk;
    }

    public void setSafe(String safe) {
        this.safe = safe;
    }

    //----------------------- getter -----------------------

    public DB getDatabase() {
        return database;
    }

    public Mongo getMongo() {
        return mongo;
    }
}
